//
// Created by cube on 2020/9/25.
//

#ifndef COMPILERDOMEA1_CONTROLLER_H
#define COMPILERDOMEA1_CONTROLLER_H

#include <atomic>
#include <functional>

#include "../../lib/Tools.h"
#include "Window.h"
#include "CongestionControl.h"

using namespace Lib;

namespace NetBaseCommon{

    template <typename T,typename Id_T = uint8_t>
    class SendController{
    public:
        struct Frame_Ack{
            T           data;
            bool        ack;
            uint8_t     number;
            Frame_Ack(const T& _data):data(_data),ack(false),number(0){}
        };

    public:
        SendController(const std::chrono::microseconds& send_timeout,const uint8_t& max_send_count,const uint32_t& defaule_pacing_rate
                ,const Id_T& default_rul_index,const Id_T& min_frame_index,const Id_T& max_frame_index,const Id_T& init_send_wnd_size)
                :__window(min_frame_index,max_frame_index,default_rul_index, nullptr,init_send_wnd_size)
                ,__control(defaule_pacing_rate)
                ,__send_timeout(send_timeout),__max_send_count(max_send_count),__default_rul_index(default_rul_index){}

        void sendFrameOnly(const T& frame,const uint32_t& size) {
            const uint64_t now = getNow();
            const uint64_t& next_send_time = __control.nextSendTime();
            if(now < next_send_time){
                std::chrono::microseconds time(next_send_time - now);
                timeEvent_sendFrame(time,frame,size);
//                sendEvent(time,std::make_shared<Time_SendFrame_Event>(this,frame,size));
            }else{
                sendToIO(frame);
                __control.send_only(size,now);
            }
        }

        bool canSend(){return __window.canSend();}

        Id_T sendFrame(const T& frame,const uint32_t& size){
            if(!__window.canSend())return __default_rul_index;
            const uint64_t now = getNow();
            auto frame_p = new Frame_Ack(frame);
            __control.send(size,now);
            Id_T id = __window.send(frame_p);
            sendFrameAck(id,frame_p);
            return id;
        }

        void onAck(const Id_T& id,const uint32_t& size){
            Frame_Ack* frame_p = __window.onAck(id);
            if(frame_p == nullptr)return;
            __control.recv(size);
            frame_p->ack = true;
        }

        void service(const uint32_t& rtt){
            uint32_t window;
            __control.service(window,rtt,getNow());
            __window.reSize(window);
        }

        void clear(){
            __window.clear();
            __control.clear();
        }

    protected:
        void sendFrameAck(Id_T id,Frame_Ack *frame) {
            const uint64_t now = getNow();
            const uint64_t& next_send_time = __control.nextSendTime();
            if(now < next_send_time){
                std::chrono::microseconds time(next_send_time - now);
                timeEvent_sendFrameAck(time,id,frame);
//                sendEvent(time,std::make_shared<Time_SendAckFrame_Event>(this,id,frame));
            }else{
                sendToIO(id,frame->data);
                timeEvent_sendTimeout(__send_timeout,id,frame);
//                sendEvent(__send_timeout,std::make_shared<SendFrame_TimeOut_Event>(this,id,frame));
            }
        }

        void ackTimeout(Id_T id,Frame_Ack *frame) {
            if(frame->ack){
                delete frame;
                return;
            }else if(frame->number >= __max_send_count){
                delete frame;
                sendTimeout();
                return;
            }else{
                __control.addLost(1);
                frame->number++;
                sendFrameAck(id,frame);
            }
        }

    protected:
        virtual void timeEvent_sendFrameAck(std::chrono::microseconds time,Id_T id,Frame_Ack *frame){}
        virtual void timeEvent_sendTimeout(std::chrono::microseconds time,Id_T id,Frame_Ack *frame){}
        virtual void timeEvent_sendFrame(std::chrono::microseconds time,const T& frame,const uint32_t& size){}

        virtual void sendToIO(const Id_T& id,const T& frame){}
        virtual void sendToIO(const T& frame){}
        virtual void sendTimeout(){}

    protected:
        SendWindow<Frame_Ack*,Id_T>     __window;
        CongestionControl               __control;

        const std::chrono::microseconds& __send_timeout;
        const uint8_t&                  __max_send_count;
        const uint8_t&                  __default_rul_index;
    };
}


#endif //COMPILERDOMEA1_CONTROLLER_H
